package main

import (
	"fmt"
	"os"
	"reflect"
	"strconv"
)

var i = 5
var str = "ABC"

type Person struct {
	name string
	age int
}

type Any interface {

}

func reflectTest() {
	var x float64 = 3.4
	fmt.Println("Type: ", reflect.TypeOf(x))
	v := reflect.ValueOf(x)
	fmt.Println("Value: ", v)
	fmt.Println("type: ", v.Type())
	fmt.Println("Kind: ", v.Kind())
	fmt.Println("value: ", v.Float())
	fmt.Println(v.Interface())
	fmt.Printf("value is %5.2e\n", v.Interface())
	y := v.Interface().(float64)
	fmt.Println(y)
}

func reflectTest2() {
	var x float64 = 3.14
	v := reflect.ValueOf(x)
	fmt.Println("settability of v: ", v.CanSet())
	v = reflect.ValueOf(&x)
	fmt.Println("type of v: ", v.Type())
	fmt.Println("settability of v: ", v.CanSet())
	v = v.Elem()
	fmt.Println("The Elem of v is: ", v)
	fmt.Println("settability of v: ", v.CanSet())
	v.SetFloat(3.1415)
	fmt.Println(v.Interface())
	fmt.Println(x)
}

type NotKnowType struct {
	s1, s2, s3 string
}

func (n NotKnowType) String() string {
	return n.s1 + " - " + n.s2 + " - " + n.s3
}

var secret  interface{} = NotKnowType{"Ada", "Go", "Oberon"}

func reflectTest3() {
	Value := reflect.ValueOf(secret)
	fmt.Println(Value)
	typ := reflect.TypeOf(secret)
	fmt.Println(typ)

	knd := Value.Kind()
	fmt.Println(knd)

	for i := 0; i < Value.NumField(); i++ {
		fmt.Printf("Field %d: %v\n", i, Value.Field(i))
	}

	results := Value.Method(0).Call(nil)
	fmt.Println(results)
}

type T struct {
	A int
	B string
}

func reflectTest4() {
	t := T{23, "skidao"}
	s := reflect.ValueOf(&t).Elem()
	typeOfT := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
	}

	s.Field(0).SetInt(77)
	s.Field(1).SetString("Sunset Strip")
	fmt.Println("t is now: ", t)
}

type Stringer interface {
	String() string
}

type Celsius float64

func (c Celsius) String() string {
	return strconv.FormatFloat(float64(c), 'f', 1, 64) + "C"
}

type Day int
var dayName = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}

func (day Day) String() string {
	return dayName[day]
}

func print(args ...interface{}) {
	for i, arg :=range args{
		if i > 0{
			os.Stdout.WriteString(" ")
		}
		switch a := arg.(type) {
		case Stringer:
			os.Stdout.WriteString(a.String())
			break
		case int:
			os.Stdout.WriteString(strconv.Itoa(a))
			break
		case string:
			os.Stdout.WriteString(a)
			break
		default:

		}
	}
}

type IDuck interface {
	Quack()
	Walk()
}

func DuckDance(duck IDuck) {
	for i := 1; i <= 3; i++ {
		duck.Quack()
		duck.Walk()
	}
}

type Bird struct {

}

func (b *Bird) Quack() {
	fmt.Println("I am quacking")
}

func (b *Bird) Walk() {
	fmt.Println("I am walking")
}

func test() {
	b := new(Bird)
	DuckDance(b)
}

type Shaper interface {
	Area() float32
}

type TopologicalGenus interface {
	Rank() int
}

type Square struct {
	side float32
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

func (sq *Square) Rank() int {
	return 1
}

type Rectangle struct {
	length, width float32
}

func (r Rectangle) Area() float32 {
	return r.length * r.width
}

func (r Rectangle) Rank() int {
	return 2
}

func test2() {
	r := Rectangle{5, 3}
	q := &Square{5}

	shapes := []Shaper{r, q}
	fmt.Println("Looping through shapes for ares ...")
	for n, _:=range shapes{
		fmt.Println("Shape details: ", shapes[n])
		fmt.Println("Area of this shape is: ", shapes[n].Area())
	}

	topgen := []TopologicalGenus{r, q}
	fmt.Println("Looping through topgen for rank ...")
	for n, _ :=range topgen{
		fmt.Println("Shape details: ", topgen[n])
		fmt.Println("Topological Genus of this shape is: ", topgen[n].Rank())
	}
}

func main() {
	var val Any
	val = 5
	fmt.Printf("val has the value: %v\n", val)
	val = str
	fmt.Printf("val has the value: %v\n", val)
	pers1 := new(Person)
	pers1.name = "Rob Pike"
	pers1.age = 55
	val = pers1
	fmt.Printf("val has the value: %v\n", val)
	switch t := val.(type) {
	case int:
		fmt.Printf("Type int %T\n", t)
		break
	case string:
		fmt.Printf("Type string %T\n", t)
		break
	case *Person:
		fmt.Printf("Type pointer to Person %T\n", t)
		break
	default:
		fmt.Printf("Unexpected type %T\n", t)
	}

	reflectTest()
	reflectTest2()
	reflectTest3()
	reflectTest4()

	print(Day(1), "was", Celsius(18.38))
	test()
	test2()
}
